﻿namespace Financeiro.Domain.Entities {
    using System;
    using Financeiro.Domain.Exceptions;

    public abstract class Lancamento {
        public virtual int Id { get; set; }
        public virtual string Comentario { get; set; }
        public virtual TipoLancamento Tipo { get; set; }
        public virtual DateTime Vencimento { get; set; }
        public virtual double Valor { get; protected set; }
        public virtual Conta Conta { get; set; }
        public virtual TipoSituacao Situacao { get; set; }
        public virtual Favorecido Favorecido { get; set; }
        public virtual Categoria Categoria { get; set; }
        public virtual Parcela Parcela { get; set; }

        protected Lancamento(){}

        protected Lancamento(double valor, string comentario, Favorecido favorecido, TipoSituacao situacao, DateTime vencimento, Parcela parcela){
            this.SetValor(valor);
            this.Comentario = comentario;
            this.Favorecido = favorecido;
            this.Situacao = situacao;
            this.Vencimento = Convert.ToDateTime(vencimento.ToShortDateString());
            this.Parcela = parcela;

            if (vencimento < Convert.ToDateTime(DateTime.Now.ToShortDateString()) && situacao == TipoSituacao.ABERTO) 
                throw new BusinessException("Um agendamento não pode ter uma data anterior.");
            if (parcela.Atual > parcela.Total)
                throw new BusinessException("Parcela inválida!");
        }

        public virtual void Consolidar() {
            this.Situacao = TipoSituacao.CONSOLIDADO;
        }

        public virtual int GetMes {
            get { return Vencimento.Month; }
        }

        public virtual int GetYear {
            get { return Vencimento.Year; }
        }

        public abstract void SetValor(double valor);

        public static L GetLancamento<L>(double valor, string comentario, Favorecido favorecido, TipoSituacao situacao, DateTime vencimento, Parcela parcela) 
            where L : Lancamento {

            object[] args = { 
                valor,
                comentario, 
                favorecido, 
                situacao, 
                vencimento,
                parcela
            };

            return (L)Activator.CreateInstance(typeof(L), args);
        }

        public static Lancamento GetLancamento(double valor, string comentario, Favorecido favorecido, TipoSituacao situacao, DateTime vencimento, TipoLancamento tipo, Parcela parcela) {

            object[] args = { 
                valor,
                comentario, 
                favorecido, 
                situacao, 
                vencimento,
                parcela
            };

            if (tipo == TipoLancamento.SAQUE)
                return (Saque)Activator.CreateInstance(typeof(Saque), args);

            return (Deposito)Activator.CreateInstance(typeof(Deposito), args);
        }
    }
}